use crate::GeometryTool::GeometryTool;
use crate::Line2::Line2;
use crate::MathUtils::MathUtils;
use crate::Matrix3x3::Matrix3x3;
use crate::Point::Point;
use crate::Polygon::Polygon;
use crate::Rect::Rect;
use crate::Vector2::Vector2;

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Segment {
    pub start: Point,
    pub end: Point,
    pub dy: f64,
    pub dx: f64,
}

impl Segment {
    pub fn new(start: Point, end: Point) -> Self {
        let dy = end.y - start.y;
        let dx = end.x - start.x;
        Self { start, end, dy, dx }
    }

    pub fn line(&self) -> Line2 {
        Line2::new_with_point(self.start, self.end)
    }

    pub fn from(&self) -> Point {
        self.start
    }

    pub fn set_from(&mut self, val: Point) {
        self.start = val;
    }

    pub fn to(&self) -> Point {
        self.end
    }

    pub fn set_to(&mut self, val: Point) {
        self.end = val;
    }

    pub fn center(&self) -> Point {
        Point::new(
            (self.start.x + self.end.x) * 0.5,
            (self.start.y + self.end.y) * 0.5,
        )
    }

    pub fn dir(&self) -> Vector2 {
        Vector2::new(self.dx, self.dy)
    }

    pub fn negative_dir(&self) -> Vector2 {
        if self.start.y > self.end.y {
            self.end.subtract(&self.start)
        } else {
            self.start.subtract(&self.end)
        }
    }

    pub fn max_y(&self) -> f64 {
        if self.start.y > self.end.y {
            self.start.y
        } else {
            self.end.y
        }
    }

    pub fn min_y(&self) -> f64 {
        if self.start.y < self.end.y {
            self.start.y
        } else {
            self.end.y
        }
    }

    pub fn rad(&self) -> f64 {
        let p = self.dx / (self.dx.abs() + self.dy.abs()); // -1 .. 1 increasing with x
        if self.dy < 0.0 {
            p - 1.0 // -2 .. 0 increasing with x
        } else {
            1.0 - p
        }
    }

    pub fn Rect(&self) -> Rect {
        Rect::new(
            Point::new(self.start.x.min(self.end.x), self.start.y.min(self.end.y)),
            Point::new(self.start.x.max(self.end.x), self.start.y.max(self.end.y)),
        )
    }

    pub fn length(&self) -> f64 {
        self.dir().length()
    }

    pub fn is_zero(&self, epsilon: Option<f64>) -> bool {
        self.start.equal_to(&self.end, epsilon)
    }

    pub fn left_seg(&self, offset: f64) -> Segment {
        let nor = self.dir().ccw_normal().normalize();
        let translate_v = nor.multiply(offset);
        let new_start = self.start.translate(&translate_v);
        let new_end = self.end.translate(&translate_v);
        Segment::new(new_start, new_end)
    }

    pub fn is_vertical(&self, epsilon: Option<f64>) -> bool {
        MathUtils::equal(self.start.x, self.end.x, epsilon) || // 两个点的x值非常接近，主要应对的是线段很短的情况
            MathUtils::equal_0(self.dir().normalize().x, epsilon)
    }

    pub fn is_horizontal(&self, epsilon: Option<f64>) -> bool {
        MathUtils::equal(self.start.y, self.end.y, epsilon) || // 两个点的y值非常接近，主要应对的是线段很短的情况
            MathUtils::equal_0(self.dir().normalize().y, epsilon)
    }

    pub fn contain_seg(&self, seg: &Segment, epsilon: Option<f64>) -> bool {
        self.contain(&seg.start, epsilon) && self.contain(&seg.end, epsilon)
    }

    pub fn equal_to(&self, other: &Segment, epsilon: Option<f64>) -> bool {
        (self.start.equal_to(&other.start, epsilon) && self.end.equal_to(&other.end, epsilon))
            || (self.end.equal_to(&other.start, epsilon)
                && self.start.equal_to(&other.end, epsilon))
    }

    pub fn contain(&self, p: &Point, epsilon: Option<f64>) -> bool {
        if self.on_end(p, epsilon) {
            return true;
        }
        if (p.y <= self.start.y && p.y >= self.end.y) || (p.y >= self.start.y && p.y <= self.end.y)
        {
            if MathUtils::equal(self.start.y, self.end.y, epsilon) {
                return (p.x < self.start.x) != (p.x < self.end.x);
            }
            let p_x = ((self.start.x - self.end.x) * (p.y - self.end.y))
                / (self.start.y - self.end.y)
                + self.end.x;
            if MathUtils::equal(p.x, p_x, epsilon) {
                return true;
            }
        }
        false
    }

    pub fn move_dir(&mut self, offset: &Vector2) {
        self.start.move_point(offset);
        self.end.move_point(offset);
    }

    pub fn intersect_base_shear(&self, other: &Segment) -> Option<Point> {
        let mut mat = Matrix3x3::new();
        mat.shear_tf_x(-self.slope());
        let mut binding = mat.clone();
        let invert_mat = binding.invert();
        let (s_x, s_y) = mat.apply(self.start.x, self.start.y);
        let (e_x, e_y) = mat.apply(self.end.x, self.end.y);
        let shear_seg = Segment::new(Point::new(s_x, s_y), Point::new(e_x, e_y));
        let y = shear_seg.start.y;
        let x_min = if shear_seg.start.x < shear_seg.end.x {
            shear_seg.start.x
        } else {
            shear_seg.end.x
        };
        let x_max = if shear_seg.start.x >= shear_seg.end.x {
            shear_seg.start.x
        } else {
            shear_seg.end.x
        };
        let (start_x, start_y) = mat.apply(other.start.x, other.start.y);
        let (end_x, end_y) = mat.apply(other.end.x, other.end.y);
        if (y <= start_y && y >= end_y) || (y <= end_y && y >= start_y) {
            if end_y == start_y {
                return None;
            }
            let i_x = start_x + ((end_x - start_x) * (y - start_y)) / (end_y - start_y);
            if i_x <= x_max && i_x >= x_min {
                let i_p = Point::new(i_x, y);
                let origin = invert_mat.apply(i_p.x, i_p.y);
                return Some(Point::new(origin.0, origin.1));
            }
            return None;
        }
        return None;
    }

    pub fn is_intersect(&self, other: &Segment, include_end: bool) -> bool {
        if include_end {
            return !GeometryTool::same_side(&self.start, &self.end, &other.start, &other.end)
                && !GeometryTool::same_side(&other.start, &other.end, &self.start, &self.end);
        }
        return !GeometryTool::same_side_sim(&self.start, &self.end, &other.start, &other.end)
            && !GeometryTool::same_side_sim(&other.start, &other.end, &self.start, &self.end);
    }
    #[no_mangle]
    pub extern "C" fn intersect(
        &self,
        other: &Segment,
        include_end: bool,
        ep: Option<f64>,
    ) -> Option<Point> {
        let a_start = &self.start;
        let b_start = &other.start;
        let p0_x = a_start.x;
        let p0_y = a_start.y;
        let p2_x = b_start.x;
        let p2_y = b_start.y;
        let s1_x = self.start.x - self.end.x;
        let s1_y = self.start.y - self.end.y;
        let s2_x = other.start.x - other.end.x;
        let s2_y = other.start.y - other.end.y;
        let div = s1_x * s2_y - s2_x * s1_y;
        if MathUtils::equal_0(div, ep) {
            return None;
        }
        let s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div;
        if MathUtils::less(s, 0.0, ep) || MathUtils::greater(s, 1.0, ep) {
            return None;
        }
        let t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div;
        if MathUtils::less(t, 0.0, ep) || MathUtils::greater(t, 1.0, ep) {
            return None;
        }
        if include_end {
            return Some(Point::new(p0_x - t * s1_x, p0_y - t * s1_y));
        } else {
            let p = Point::new(p0_x - t * s1_x, p0_y - t * s1_y);
            if self.on_end(&p, ep) || other.on_end(&p, ep) {
                return None;
            }
            return Some(p);
        }
    }
    pub fn intersectSegs(&self, segs: &Vec<Segment>, ep: Option<f64>) -> Vec<Point> {
        let mut ips: Vec<Point> = Vec::new();
        for item in segs.iter() {
            let ip = self.intersect(item, false, ep);
            if let Some(p) = ip {
                ips.push(p);
            }
        }
        ips
    }

    pub fn touch(&self, other: &Segment, ep: Option<f64>) -> Option<Point> {
        if self.is_intersect(other, false) {
            return None;
        }
        let tangent = self.tangent(other, ep);
        if let Some(seg) = tangent {
            return Some(seg.start);
        }
        if other.contain(&self.start, ep) {
            return Some(self.start);
        }
        if other.contain(&self.end, ep) {
            return Some(self.end);
        }
        if self.contain(&other.start, ep) {
            return Some(other.start);
        }
        if self.contain(&other.end, ep) {
            return Some(other.end);
        }
        None
    }

    pub fn tangent(&self, other: &Segment, ep: Option<f64>) -> Option<Segment> {
        if !self.parallel(other, ep) {
            return None;
        }
        if self.contain(&other.start, ep) {
            if self.contain(&other.end, ep) {
                return Some(other.clone());
            } else {
                if other.contain(&self.end, ep) {
                    return Some(Segment::new(other.start.clone(), self.end.clone()));
                } else {
                    if other.contain(&self.start, ep) {
                        return Some(Segment::new(other.start.clone(), self.start.clone()));
                    } else {
                        panic!("我怀疑不平行，说了要已经平行的！！！");
                    }
                }
            }
        } else {
            if self.contain(&other.end, ep) {
                if other.contain(&self.end, ep) {
                    return Some(Segment::new(other.end.clone(), self.end.clone()));
                } else {
                    if other.contain(&self.start, ep) {
                        return Some(Segment::new(other.end.clone(), self.start.clone()));
                    } else {
                        panic!("我怀疑不平行，说了要已经平行的！！！");
                    }
                }
            } else {
                return None;
            }
        }
    }

    pub fn tangent_vh(&self, other: &Segment, ep: Option<f64>) -> Option<Segment> {
        if self.is_vertical(ep)
            && other.is_vertical(ep)
            && MathUtils::equal(self.start.x, other.start.x, ep)
        {
            let y_seg =
                MathUtils::overlap(self.start.y, self.end.y, other.start.y, other.end.y, ep);
            if let Some(y_seg) = y_seg {
                let p0 = Point::new(self.start.x, y_seg[0]);
                let p1 = Point::new(self.start.x, y_seg[1]);
                return Some(Segment::new(p0, p1));
            } else {
                return None;
            }
        }

        if self.is_horizontal(ep)
            && other.is_horizontal(ep)
            && MathUtils::equal(self.start.y, other.start.y, ep)
        {
            let x_seg =
                MathUtils::overlap(self.start.x, self.end.x, other.start.x, other.end.x, ep);
            if let Some(x_seg) = x_seg {
                let p0 = Point::new(x_seg[0], self.start.y);
                let p1 = Point::new(x_seg[1], self.start.y);
                return Some(Segment::new(p0, p1));
            } else {
                return None;
            }
        }
        return None;
    }

    pub fn parallel(&self, other: &Segment, epsilon: Option<f64>) -> bool {
        if other.is_zero(epsilon) {
            return false;
        }
        return MathUtils::equal_0(
            self.dir().normalize().cross(&other.dir().normalize()),
            epsilon,
        );
    }

    pub fn clone(&self) -> Segment {
        return Segment::new(self.start.clone(), self.end.clone());
    }

    pub fn slope(&self) -> f64 {
        return (self.end.y - self.start.y) / (self.end.x - self.start.x);
    }

    pub fn split(&self, p: &Point, ep: Option<f64>) -> [Option<Segment>; 2] {
        if self.start.equal_to(p, ep) {
            return [None, Some(self.clone())];
        }
        if self.end.equal_to(p, ep) {
            return [Some(self.clone()), None];
        }
        return [
            Some(Segment::new(self.start.clone(), p.clone())),
            Some(Segment::new(p.clone(), self.end.clone())),
        ];
    }

    /**
     * 点到线段的距离
     */
    pub fn distance_to_point(&self, p: &Point, ep: Option<f64>) -> (f64, Point) {
        if self.start.equal_to(&self.end, ep) {
            //线段缩成一个点？
            return (p.distance_to_point(&self.start), self.start.clone());
        }
        let closest_point = Line2::new_with_point(self.start, self.end).closest_point(p);
        let seg_v = self.dir();
        let start_v = closest_point.subtract(&self.start);
        let end_v = closest_point.subtract(&self.end);
        let start_dot = start_v.dot(&seg_v);
        let end_dot = -end_v.dot(&seg_v);
        if start_dot >= 0.0 && end_dot >= 0.0 {
            let dist = p.distance_to_point(&closest_point);
            return (dist, closest_point);
        } else if start_dot < 0.0 {
            /* point is out of scope closer to ps */
            return (p.distance_to_point(&self.start), self.start.clone());
        } else {
            /* point is out of scope closer to pe */
            return (p.distance_to_point(&self.start), self.end.clone());
        }
    }

    /**
     * 线段是否在线段范围内,包括端点
     */
    pub fn in_segment_scope(&self, p: &Point) -> bool {
        let seg_v = self.end.subtract(&self.start);
        let start_v = p.subtract(&self.start);
        let end_v = p.subtract(&self.end);
        let start_dot = start_v.dot(&seg_v);
        let end_dot = -end_v.dot(&seg_v);
        start_dot >= 0.0 && end_dot >= 0.0
    }

    /**
     * 点是否在线段的两侧端点上
     */
    pub fn on_end(&self, p: &Point, ep: Option<f64>) -> bool {
        p.equal_to(&self.start, ep) || p.equal_to(&self.end, ep)
    }

    /**
     * 共线
     */
    pub fn collinear(&self, seg: &Segment, epsilon: Option<f64>) -> bool {
        if !self.parallel(seg, epsilon) {
            return false;
        }
        MathUtils::equal_0(
            Line2::new_with_point(self.start, self.end).distance_to_line(&seg.start),
            epsilon,
        )
    }
    /**
     * @author lianbo
     * @date 2020-11-24 21:05:00
     * @Description: 一条线段减去另一条线段，共线的时候
     */
    pub fn subtract(&self, other: &Segment, epsilon: Option<f64>) -> Vec<Segment> {
        if (!self.collinear(other, epsilon)) {
            return vec![self.clone()];
        }
        if (other.contain(&self.start, epsilon) && other.contain(&self.end, epsilon)) {
            return vec![];
        }
        if (self.contain(&other.start, epsilon) && self.contain(&other.end, epsilon)) {
            if (Segment::new(self.start, other.start).contain(&other.end, epsilon)) {
                return vec![
                    Segment::new(self.start, other.end),
                    Segment::new(other.start, self.end),
                ];
            }
            return vec![
                Segment::new(self.start, other.start),
                Segment::new(other.end, self.end),
            ];
        }
        if (self.contain(&other.start, epsilon) && other.contain(&self.end, epsilon)) {
            return vec![Segment::new(self.start, other.start)];
        }
        if (self.contain(&other.start, epsilon) && other.contain(&self.start, epsilon)) {
            return vec![Segment::new(other.start, self.end)];
        }

        if (self.contain(&other.end, epsilon) && other.contain(&self.end, epsilon)) {
            return vec![Segment::new(self.start, other.end)];
        }
        if (self.contain(&other.end, epsilon) && other.contain(&self.start, epsilon)) {
            return vec![Segment::new(other.end, self.end)];
        }
        return vec![self.clone()];
    }
    /**
     * 被多条线段去减，看剩余的线段
     */
    pub fn subtracted(&self, others: &[Segment], epsilon: Option<f64>) -> Vec<Segment> {
        let mut difference: Vec<Segment> = vec![self.clone()];
        for other in others {
            if difference.len() == 0 {
                break;
            }
            let mut result: Vec<Segment> = vec![];
            for seg in difference {
                let dif = seg.subtract(other, epsilon);
                if dif.len() > 0 {
                    for item in dif {
                        if !item.is_zero(epsilon) {
                            result.push(item);
                        }
                    }
                }
            }
            difference = result;
        }
        return difference;
    }

    /**
     * 点的水平线与线段及线段延长线的交点
     */
    pub fn intersection_x(&self, p: &Point, ep: Option<f64>) -> Option<f64> {
        if self.is_horizontal(ep) {
            if !MathUtils::equal(p.y, self.start.y, ep) {
                return None;
            }
            let min_x = if self.start.x > self.end.x {
                self.end.x
            } else {
                self.start.x
            };
            let max_x = if self.start.x < self.end.x {
                self.end.x
            } else {
                self.start.x
            };
            if p.x < min_x {
                return Some(min_x);
            }
            if p.x > max_x {
                return Some(max_x);
            }
            return Some(p.x);
        }
        return Some(
            Line2::new_with_point(self.start.clone(), self.end.clone()).get_x_from_y(p.y, ep),
        );
    }

    /**
     * 点的垂直射线与线段及线段延长线的交点
     */
    pub fn intersection_y(&self, p: &Point, ep: Option<f64>) -> Option<f64> {
        if self.is_vertical(ep) {
            if !MathUtils::equal(p.x, self.start.x, ep) {
                return None;
            }
            let min_y = if self.start.y > self.end.y {
                self.end.y
            } else {
                self.start.y
            };
            let max_y = if self.start.y < self.end.y {
                self.end.y
            } else {
                self.start.y
            };
            if p.y < min_y {
                return Some(min_y);
            }
            if p.y > max_y {
                return Some(max_y);
            }
            return Some(p.y);
        }
        return Some(Line2::new_with_point(self.start, self.end).get_y_from_x(p.x, ep));
    }

    /**
     * 按x的大小输出点
     */
    pub fn by_x(&self) -> [Point; 2] {
        if self.start.x < self.end.x {
            return [self.start.clone(), self.end.clone()];
        }
        return [self.end.clone(), self.start.clone()];
    }
    /// 平移
    pub fn translate(&self, dis: f64) -> Segment {
        let v = self.dir().ccw_normal().normalize().multiply(dis);
        let start = self.start.translate(&v);
        let end = self.start.translate(&v);
        return Segment::new(start, end);
    }

    /**
     * 线段膨胀成一个polygon
     */
    pub fn offset(&self, dis: f64) -> Polygon {
        let mut v = self.dir().ccw_normal().normalize().multiply(dis);
        let start = self.start.translate(&v);
        let end = self.end.translate(&v);
        v.invert();
        let start1 = self.start.translate(&v);
        let end1 = self.end.translate(&v);
        return Polygon::new(vec![start, end, end1, start1]);
    }

    pub fn shorten(&self, dis: f64) -> Segment {
        let mut v = self.dir().normalize().multiply(dis);
        let new_start = self.start.translate(&v);
        if dis >= self.length() {
            return Segment::new(new_start, new_start);
        }
        v.invert();
        let new_end = self.end.translate(&v);
        return Segment::new(new_start, new_end);
    }
}
